package hu.test.atacama;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.widget.Toast;

import com.google.android.gms.maps.model.MarkerOptions;

public class Common {

	private static Common instance;
	private static Context context;

	public static final String ARDUINO_BLUETOOTH_NAME = "HC-06";
	public static final String ARDUINO_BLUETOOTH_MAC = "20:13:09:10:00:32";
	public static final String ARDUINO_BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB";
	public static final String COMMAND_BLUETOOTH_LOCK = "lockDoor";
	public static final String COMMAND_BLUETOOTH_UNLOCK = "unlockDoor";
	public static final String COMMAND_STORE_GPS_DATA = "storeGPSData";
	public static final String COMMAND_REGISTER = "registerDevice";
	public static final String COMMAND_STORE_SERIALNUMBER = "storeSerialNumber";
	public static final String COMMAND_SOS = "SOS";
	public static final String COMMAND_GETALLDEVICES = "getAllDevices";
	public static final String COMMAND_ADDMARKER = "addMarker";
	public static final String COMMAND_SHOWMAP = "showMap";
	public static final String COMMAND_GETCARLIST = "getCarList";
	public static final String COMMAND_SHOWCARLIST = "showCarList";
	public static final String COMMAND_CHANGEARDUINOMODE = "showCarList";
	public static final String COMMAND_GET_PATH = "getPath";
	public static final String COMMAND_SHOW_PATH = "showPath";
	public static final String COMMAND_GET_SENSOR_VALUES = "getSensorValues";
	public static final String COMMAND_SHOW_SENSOR_VALUES = "showSensorValues";	
	public static final String NO_PHOTO = "noPhoto";
	public static final String OWN_KEY = "ownKey";
	public static final String SEPARATOR = ";;;";
	public static final String FILE_SAVED_LOCATIONS = "atacama.loc";
	public static final String FILE_CARS = "atacama.cars";
	public static final String PATH_ARRAY_FILL_COMPLETE = "hu.test.atacama.PATH_DRAW_COMPLETE";
	public static final int REQUEST_ENABLE_BT = 1;
	public static final int NUMBER_OF_PAGES = 2;

	public boolean synchronizeGPS = false;
	public MarkerOptions[] markers;

	LocationManager mLocationManager = null;
	
	public List<Car> carList = new ArrayList<Car>();
	public List<Sensor> sensorList = new ArrayList<Sensor>();
	private List<Coordinates> pathData = new ArrayList<Coordinates>();	

	public interface mButtonClickListener {
		public void buttonClicked(int buttonID);
	}

	public static Common getInstance(Context c) {
		context = c;
		if (instance == null) {
			instance = new Common();
		}
		return instance;
	}

	private Common() {

	}

	public void setMarkers(MarkerOptions[] markers) {
		this.markers = markers;
	}

	public List<Coordinates> getPathData() {
		return this.pathData;
	}
	
	public void clearPathData() {
		this.pathData.clear();
	}
	
	public void setPathData(List<Coordinates> pathData) {
		this.pathData = pathData;
		Intent broadcast = new Intent(PATH_ARRAY_FILL_COMPLETE);
		context.sendBroadcast(broadcast);
	}

	public void displayToast(String message) {
		Toast.makeText(context, message, Toast.LENGTH_LONG).show();
	}

	public void setRefreshTime(long minTime) {
		updateSharedPreferenceByKey(
				context.getString(R.string.key_refresh_time),
				Long.toString(minTime));
	}

	public void setRefreshDistance(float minDistance) {
		updateSharedPreferenceByKey(
				context.getString(R.string.key_refresh_distance),
				Float.toString(minDistance));
	}

	public void setOperationMode(String mode) {
		updateSharedPreferenceByKey(
				context.getString(R.string.key_operation_mode), mode);
	}

	public void setSynchronizeGPS(boolean condition) {
		this.synchronizeGPS = condition;
	}

	public void showAlert(String message) {
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		builder.setMessage(message).setCancelable(false)
				.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						// do things
					}
				});
		AlertDialog alert = builder.create();
		alert.show();
	}

	public boolean isOnline() {
		ConnectivityManager cm = (ConnectivityManager) context
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo netInfo = cm.getActiveNetworkInfo();
		if (netInfo != null && netInfo.isConnected()) {
			return true;
		}
		return false;
	}

	public boolean isUnuploadedData() {
		boolean fileExist = false;
		try {
			if (isExternalStorageAvailable()) {
				File file = new File(context.getExternalFilesDir(null),
						FILE_SAVED_LOCATIONS);
				if (file.exists()) {
					fileExist = true;
				}
			} else {
				File file = context.getFileStreamPath(FILE_SAVED_LOCATIONS);
				if (file.exists()) {
					fileExist = true;
				}
			}
		} catch (Exception ex) {
			displayToast("Common / isUnuploadedData: " + ex.toString());
		}
		return fileExist;
	}

	public String getSharedPreferenceByKey(String key, String defaultKey) {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(context);
		return prefs.getString(key, defaultKey);
	}

	public void updateSharedPreferenceByKey(String key, String value) {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(context);
		prefs.edit().putString(key, value).commit();
	}

	public void storeGPSPosition(Location location) {
		updateSharedPreferenceByKey(
				context.getString(R.string.key_last_known_gps_lat),
				Double.toString(location.getLatitude()));
		updateSharedPreferenceByKey(
				context.getString(R.string.key_last_known_gps_lon),
				Double.toString(location.getLongitude()));
		updateSharedPreferenceByKey(
				context.getString(R.string.key_last_known_gps_time),
				Long.toString(location.getTime()));
	}

	public String getLastKnownLatitude() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_last_known_gps_lat),
				context.getString(R.string.default_last_known_gps_lat));
	}

	public String getLastKnownLongitude() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_last_known_gps_lon),
				context.getString(R.string.default_last_known_gps_lon));
	}

	public String getLastKnownTime() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_last_known_gps_time),
				context.getString(R.string.default_last_known_gps_time));
	}
	
	public String getCarLastKnownCoordinates() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_current_car_last_known_coordinates),
				context.getString(R.string.default_current_car_last_known_coordinates));
	}
	
	public void setCarLastKnownCoordinates(String lastKnownCoordinates) {
		updateSharedPreferenceByKey(context.getString(R.string.key_current_car_last_known_coordinates),
				lastKnownCoordinates);
	}

	// public void loadSharedPreferences() {

	/*
	 * StringBuilder builder = new StringBuilder();
	 * builder.append("deviceType: " + deviceType + "\n");
	 * builder.append("senderID: " + senderID + "\n");
	 * builder.append("serialNumber: " + serialNumber + "\n");
	 * builder.append("serverAddress: " + serverAddress + "\n");
	 * showAlert(builder.toString());
	 */
	// }

	public String getDeviceType() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_device_type),
				context.getString(R.string.default_device_type));
	}

	public String getOperationMode() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_operation_mode),
				context.getString(R.string.default_operation_mode));
	}

	public String getSenderID() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_sender_id),
				context.getString(R.string.default_sender_id));
	}

	public String getSerialNumber() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_serial_number),
				context.getString(R.string.default_serial_number));
	}

	public String getServerAddress() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_server_address),
				context.getString(R.string.default_server_address));
	}

	public String getCurrentCarKey() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_current_car_key),
				context.getString(R.string.default_current_car_key));
	}

	public String getCurrentCarName() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_current_car_name),
				context.getString(R.string.default_current_car_name));
	}

	public void setCurrentCarName(String carName) {
		updateSharedPreferenceByKey(
				context.getString(R.string.key_current_car_name), carName);
	}

	public String getNumberOfWaypoints() {
		return getSharedPreferenceByKey(
				context.getString(R.string.key_path_waypoints),
				context.getString(R.string.default_path_waypoints));
	}
	
	public void setNumberOfWaypoints(String numberOfWaypoints) {
		updateSharedPreferenceByKey(
				context.getString(R.string.key_path_waypoints),
				numberOfWaypoints);
	}
	
	public Long getGPSRefreshMinTime() {
		return Long.parseLong(getSharedPreferenceByKey(
				context.getString(R.string.key_refresh_time),
				context.getString(R.string.default_gps_refresh_min_time)));
	}

	public Float getGPSRefreshMinDistance() {
		return Float.parseFloat(getSharedPreferenceByKey(
				context.getString(R.string.key_refresh_distance),
				context.getString(R.string.default_gps_refresh_min_distance)));
	}

	public Float getGPSMinAccuracy() {
		return Float.parseFloat(getSharedPreferenceByKey(
				context.getString(R.string.key_refresh_accuracy),
				context.getString(R.string.default_gps_refresh_min_accuracy)));
	}

	public String getCurrentProvider() {
		String provider = null;

		String locMethod = getSharedPreferenceByKey(
				context.getString(R.string.key_localization_method),
				context.getString(R.string.default_localization_method));

		if (locMethod.equals("gps")) {
			provider = LocationManager.GPS_PROVIDER;
		}
		if (locMethod.equals("network")) {
			provider = LocationManager.NETWORK_PROVIDER;
		}
		if (locMethod.equals("passive")) {
			provider = LocationManager.PASSIVE_PROVIDER;
		}
		if (locMethod.equals("auto")) {
			Criteria criteria = new Criteria();
			criteria.setAccuracy(Criteria.ACCURACY_FINE);
			criteria.setAltitudeRequired(false);
			criteria.setBearingRequired(false);
			criteria.setCostAllowed(true);
			criteria.setPowerRequirement(Criteria.POWER_LOW);

			mLocationManager = (LocationManager) context
					.getSystemService(Context.LOCATION_SERVICE);
			provider = mLocationManager.getBestProvider(criteria, true);
			mLocationManager = null;
		}

		return provider;
	}

	private boolean isExternalStorageAvailable() {
		boolean mExternalStorageAvailable = false;
		String state = Environment.getExternalStorageState();

		if (Environment.MEDIA_MOUNTED.equals(state)) {
			mExternalStorageAvailable = true;
		} else {
			mExternalStorageAvailable = false;
		}
		return mExternalStorageAvailable;
	}

	public void deleteSavedFiles() {
		try {
			if (isExternalStorageAvailable()) {
				File file = new File(context.getExternalFilesDir(null),
						FILE_SAVED_LOCATIONS);
				if (file.exists()) {
					file.delete();
				}
			} else {
				context.deleteFile(FILE_SAVED_LOCATIONS);
			}
		} catch (Exception ex) {
			displayToast("Common / deleteSavedFiles: " + ex.toString());
		}
	}

	public static final int sizeOf(Object object) {

		if (object == null)
			return -1;

		// Special output stream use to write the content
		// of an output stream to an internal byte array.
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

		try {
			// Output stream that can write object
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(
					byteArrayOutputStream);

			// Write object and close the output stream
			objectOutputStream.writeObject(object);
			objectOutputStream.flush();
			objectOutputStream.close();

			// Get the byte array
			byte[] byteArray = byteArrayOutputStream.toByteArray();

			// TODO can the toByteArray() method return a
			// null array ?
			return byteArray == null ? 0 : byteArray.length;
		} catch (Exception ex) {
			return -1;
		}

	}

	private static int singleEntryLength = sizeOf(Coordinates.class);// + 16;

	public List<Coordinates> loadSavedLocInfo() throws IOException {
		if (singleEntryLength < 0) {
			return null;
		}

		ObjectInputStream ois = null;
		try {
			FileInputStream fis;
			File file = new File(context.getExternalFilesDir(null),
					FILE_SAVED_LOCATIONS);

			if (isExternalStorageAvailable()) {
				if (file.exists()) {
					fis = new FileInputStream(file);
				} else {
					fis = context.openFileInput(FILE_SAVED_LOCATIONS);
				}
			} else {
				fis = context.openFileInput(FILE_SAVED_LOCATIONS);
			}

			List<Coordinates> coordinates = new ArrayList<Coordinates>();
			ois = new ObjectInputStream(fis);

			while (fis.available() >= singleEntryLength) {
				coordinates.add((Coordinates) ois.readObject());
			}

			ois.close();
			return coordinates;
		} catch (Exception ex) {
			displayToast("Common / loadSavedLocInfo: " + ex.toString());
			return null;
		} finally {
			if (ois != null) {
				ois.close();
			}
		}
	}

	class AppendingObjectOutputStream extends ObjectOutputStream {

		public AppendingObjectOutputStream(OutputStream out) throws IOException {
			super(out);
		}

		@Override
		protected void writeStreamHeader() throws IOException {
			reset();
		}
	}

	public boolean saveLocInfo(Coordinates coordinates, boolean append)
			throws IOException {
		ObjectOutputStream oos = null;
		FileOutputStream fos = null;

		try {
			File file = new File(context.getExternalFilesDir(null),
					FILE_SAVED_LOCATIONS);

			if (isExternalStorageAvailable()) {
				fos = new FileOutputStream(file, true);
			} else {
				fos = context.openFileOutput(FILE_SAVED_LOCATIONS,
						Context.MODE_APPEND);
			}
			if (append) {
				oos = new AppendingObjectOutputStream(fos);
				oos.writeObject(coordinates);
				// oos.reset();
			} else {
				oos = new ObjectOutputStream(fos);
				oos.writeObject(coordinates);
			}
			return true;
		} catch (Exception ex) {
			displayToast("Common / saveLocInfo: " + ex.toString());
			return false;
		} finally {
			if (fos != null) {
				fos.flush();
				fos.close();
			}
			if (oos != null) {
				oos.flush();
				oos.close();
			}
		}
	}

	public CharSequence[] loadCarList() {
		try {
			File file = new File(context.getExternalFilesDir(null), FILE_CARS);
			BufferedReader inputReader;

			if (isExternalStorageAvailable()) {
				if (file.exists()) {
					inputReader = new BufferedReader(new InputStreamReader(
							new FileInputStream(file)));
				} else {
					if (file.exists()) {
						inputReader = new BufferedReader(new InputStreamReader(
								context.openFileInput(FILE_CARS)));
					} else {
						return null;
					}
				}
			} else {
				inputReader = new BufferedReader(new InputStreamReader(
						context.openFileInput(FILE_CARS)));
			}

			List<String> lines = new ArrayList<String>();
			String inputString;
			while ((inputString = inputReader.readLine()) != null) {
				lines.add(inputString);
			}

			CharSequence[] cars = new CharSequence[lines.size()];
			carList.clear();
			for (int i = 0; i < lines.size(); i++) {
				String[] carInfo = lines.get(i).split(SEPARATOR);
				carList.add(new Car(carInfo[0], carInfo[1]));
				cars[i] = carInfo[1];
			}

			return cars;
		} catch (Exception ex) {
			displayToast("Common / loadCarList: " + ex.toString());
			return null;
		}
	}

	public boolean saveCarList() {
		try {
			FileOutputStream fos;
			File file = new File(context.getExternalFilesDir(null), FILE_CARS);

			if (isExternalStorageAvailable()) {
				fos = new FileOutputStream(file, false);
			} else {
				fos = context.openFileOutput(FILE_CARS, Context.MODE_PRIVATE);
			}
			for (Car car : carList) {
				fos.write((car.getCarKey() + SEPARATOR + car.getCarName() + System
						.getProperty("line.separator")).getBytes());
			}
			fos.flush();
			fos.close();

			return true;
		} catch (Exception ex) {
			displayToast("Common / saveCarList: " + ex.toString());
			return false;
		}
	}
}
